﻿/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_FUNCTORS_H__
#define __CTL_FUNCTORS_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_MATH_H__
	#error functors.h requires math.h to be included first.
#endif

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

//SummationFunctor<T, TResult>

template <typename T, typename TResult>
class SummationFunctor
{
public:
	explicit SummationFunctor(TResult& sum) noexcept : m_sum(sum)
	{
	}
	void operator()(const T& v) noexcept
	{
		m_sum += (TResult)v;
	}

private:
	TResult& m_sum;
};

//DeviationFunctor<T, TResult>

template <typename T, typename TResult>
class DeviationFunctor
{
public:
	DeviationFunctor(TResult& sum, TResult mean) noexcept : m_sum(sum), m_mean(mean)
	{
	}
	void operator()(const T& v) noexcept
	{
		m_sum += SQUARE((TResult)v - m_mean);
	}

private:
	TResult& m_sum;
	TResult  m_mean;
};

//ThresholdFunctor<T, TResult, TCompareFunc>

template <typename T, typename TResult, class TCompareFunc>
class ThresholdFunctor
{
public:
	ThresholdFunctor(const T& threshold,
			const TCompareFunc& func,
			const TResult& positive_value,
			const TResult& negative_value) noexcept
				: m_threshold(threshold), m_func(func),
				m_positive_value(positive_value),
				m_negative_value(negative_value)
	{
	}
	const TResult& operator()(const T& v) const noexcept
	{
		return m_func(v, m_threshold) ? m_positive_value : m_negative_value;
	}

private:
	const T& m_threshold;
	const TCompareFunc& m_func;
	const TResult& m_positive_value;
	const TResult& m_negative_value;
};

//RescaleFunctor<T, TResult>

template <typename T, typename TResult>
class RescaleFunctor
{
public:
	RescaleFunctor(const TResult& slope, const TResult& offset) noexcept
				: m_slope(slope), m_offset(offset)
	{
	}
	TResult operator()(const T& v) const noexcept
	{
		return (TResult)v * m_slope + m_offset;
	}

private:
	const TResult& m_slope;
	const TResult& m_offset;
};

//MaxFunctor<T, TCompareTrait>
/*! \brief Calculate the maximum value.

Calculate the maximum value.
\tparam T The element type.
\tparam TCompareTrait The compare trait type.
*/
template <typename T, class TCompareTrait>
class MaxFunctor
{
public:
	MaxFunctor(T& result, uintptr_t& index) noexcept : m_result(result), m_index(index), m_current(0)
	{
	}
	void operator()(const T& v) noexcept
	{
		if ( TCompareTrait::IsGT(v, m_result) ) {
			m_result = v;
			m_index = m_current;
		}
		++ m_current;
	}

private:
	T& m_result;
	uintptr_t& m_index;
	uintptr_t m_current;
};

//MinFunctor<T, TCompareTrait>
/*! \brief Calculate the minimum value.

Calculate the minimum value.
\tparam T The element type.
\tparam TCompareTrait The compare trait type.
*/
template <typename T, class TCompareTrait>
class MinFunctor
{
public:
	MinFunctor(T& result, uintptr_t& index) noexcept : m_result(result), m_index(index), m_current(0)
	{
	}
	void operator()(const T& v) noexcept
	{
		if ( TCompareTrait::IsLT(v, m_result) ) {
			m_result = v;
			m_index = m_current;
		}
		++ m_current;
	}

private:
	T& m_result;
	uintptr_t& m_index;
	uintptr_t m_current;
};

//MaxMinFunctor<T, TCompareTrait>
/*! \brief Calculate the maximum and minimum values.

Calculate the maximum and minimum values.
\tparam T The element type.
\tparam TCompareTrait The compare trait type.
*/
template <typename T, class TCompareTrait>
class MaxMinFunctor
{
public:
	MaxMinFunctor(T& result_max, T& result_min, uintptr_t& index_max, uintptr_t& index_min) noexcept
				: m_result_max(result_max), m_result_min(result_min),
				m_index_max(index_max), m_index_min(index_min), m_current(0)
	{
	}
	void operator()(const T& v) noexcept
	{
		if ( TCompareTrait::IsGT(v, m_result_max) ) {
			m_result_max = v;
			m_index_max = m_current;
		}
		if ( TCompareTrait::IsLT(v, m_result_min) ) {
			m_result_min = v;
			m_index_min = m_current;
		}
		++ m_current;
	}

private:
	T& m_result_max;
	T& m_result_min;
	uintptr_t& m_index_max;
	uintptr_t& m_index_min;
	uintptr_t m_current;
};

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
